#include "KnowledgeBaseWriterText.h"
#include "ToStr.h"
#include <iomanip>
#include <sstream>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int Max ( int a, int b )
{
	return a > b ? a : b;
}

static std::string IntToStr ( int value )
{
	return ToStr ( value );
}

static std::vector < std::string > StringLines ( std::string multiLineString )
{
	std::vector < std::string > lines;
	std::string currentLine;
	while ( ! multiLineString.empty () )
	{
		std::string nextChar = std::string ( 1, multiLineString.at ( 0 ) );
		multiLineString.erase ( multiLineString.begin () );
		if ( nextChar == "\n" )
		{
			lines.push_back ( currentLine );
			currentLine = "";
		}
		else
			currentLine += nextChar;
	}
	lines.push_back ( currentLine );
	return lines;
}

static int LongestLineLength ( std::string multiLineString )
{
	int longestLength = 0;
	std::vector < std::string > lines = StringLines ( multiLineString );
	for ( std::vector < std::string >::iterator it = lines.begin (); it != lines.end (); it++ )
		longestLength = Max ( int ( it->size () ), longestLength );
	return longestLength;
}

static std::string NextMeanLine ( std::vector < std::string > & lines )
{
	while ( ! lines.empty () )
	{
		std::string next = * lines.begin ();
		lines.erase ( lines.begin () );
		if ( ! next.empty () )
			return next;
	}
	return "";
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Factory < KnowledgeBaseWriterInterface > KnowledgeBaseWriterText::factory;

KnowledgeBaseWriterText::KnowledgeBaseWriterText ()
: globalAddressWidth ( 0 )
{
}

KnowledgeBaseWriterText::~KnowledgeBaseWriterText ()
{
	while ( ! fieldWriters.empty () )
	{
		fieldWriters.begin ()->Delete ();
		fieldWriters.erase ( fieldWriters.begin () );
	}
}

KnowledgeBaseWriterInterfacePtr KnowledgeBaseWriterText::Spawn 
	( TextStreamInterfacePtr argStream
	, KnowledgeBaseWriterTextLocalizationInterfacePtr argLocalization
	)
{
	KnowledgeBaseWriterTextPtr writer = factory.CreateNewObject < KnowledgeBaseWriterText > ();
	
	writer->stream = argStream;
	writer->localization = argLocalization;

	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_SeniorKey > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_SeniorType > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_LexemText > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_UpperBranch > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_LowerBranch > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_NextLevel > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_Synonym > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_AdjacentAscending > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_AdjacentDescending > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_BeforeAdjacentAscending > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_BeforeAdjacentDescending > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_GreaterAscending > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_GreaterDescending > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_LesserAscending > ( writer ) );
	writer->fieldWriters.push_back ( FieldWriter::Spawn < FieldWriter_LesserDescending > ( writer ) );

	return writer;
}

void KnowledgeBaseWriterText::CheckInnocence ()
{
	assert ( factory.ObjectsCount () == 0 );
	assert ( FieldWriter::factory.ObjectsCount () == 0 );
}

void KnowledgeBaseWriterText::Write ( KnowledgeBaseMemoryInterfacePtr argMemory )
{
	memory = argMemory;
	CalculateGlobalAddressWidth ();
	for ( std::vector < FieldWriterInterfacePtr >::iterator it = fieldWriters.begin (); it != fieldWriters.end (); ++it )
		( * it )->CalculateWidth ();
	WriteDelimiter ();
	WriteTitleBar ();
	WriteDelimiter ();
	for ( int i = 1; i < memory->ElementsCount (); i++ )
		WriteMemoryElement ( i );
	WriteDelimiter ();
}

void KnowledgeBaseWriterText::CalculateGlobalAddressWidth ()
{
	globalAddressWidth = Max ( IntToStr ( memory->ElementsCount () ).size (), LongestLineLength ( localization->GlobalAddress () ) );
}

void KnowledgeBaseWriterText::WriteDelimiter ()
{
	stream->WriteString ( "+-" + std::string ( globalAddressWidth, '-' ) );
	for ( std::vector < FieldWriterInterfacePtr >::iterator it = fieldWriters.begin (); it != fieldWriters.end (); ++it )
		( * it )->WriteDelimiter ();
	stream->WriteString ( "-+\n\r" );
}

void KnowledgeBaseWriterText::WriteTitleBar ()
{
	std::vector < std::string > globalAddressTitle = StringLines ( localization->GlobalAddress () );
	for ( std::vector < FieldWriterInterfacePtr >::iterator it = fieldWriters.begin (); it != fieldWriters.end (); ++it )
		( * it )->TitleBarPrepare ();
	
	std::string nextGlobalAddressTitleLine;

	while ( true )
	{
		nextGlobalAddressTitleLine = NextMeanLine ( globalAddressTitle );
		for ( std::vector < FieldWriterInterfacePtr >::iterator it = fieldWriters.begin (); it != fieldWriters.end (); ++it )
			( * it )->TitleBarFetchNextLine ();
		if ( nextGlobalAddressTitleLine.empty ()
		  && TitleBarCheckFieldWriters ()
		   )
		{
			break;
		}
		{
			std::stringstream ss;
			ss <<  "| " << std::setw ( globalAddressWidth ) << nextGlobalAddressTitleLine;
			stream->WriteString ( ss.str () );
		}
		for ( std::vector < FieldWriterInterfacePtr >::iterator it = fieldWriters.begin (); it != fieldWriters.end (); ++it )
			( * it )->TitleBarWrite ();
		stream->WriteString ( " |\n\r" );
	}	 
}

void KnowledgeBaseWriterText::WriteMemoryElement ( int globalAddress )
{
	{
		std::stringstream ss;	
		ss <<  "| " << std::setw ( globalAddressWidth ) << ToStr ( globalAddress );
		stream->WriteString ( ss.str () );
	}
	for ( std::vector < FieldWriterInterfacePtr >::iterator it = fieldWriters.begin (); it != fieldWriters.end (); ++it )
		( * it )->WriteMemoryElement ( globalAddress );
	stream->WriteString ( " |\n\r" );
}

bool KnowledgeBaseWriterText::TitleBarCheckFieldWriters ()
{
	for ( std::vector < FieldWriterInterfacePtr >::iterator it = fieldWriters.begin (); it != fieldWriters.end (); ++it )
		if ( ! ( * it )->TitleBarCheck () )
			return false;
	return true;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Factory < KnowledgeBaseWriterText::FieldWriterInterface > KnowledgeBaseWriterText::FieldWriter::factory;

KnowledgeBaseWriterText::FieldWriter::FieldWriter ()
: width ( 0 )
{
}

void KnowledgeBaseWriterText::FieldWriter::CalculateWidth ()
{
	width = LongestLineLength ( GetLocalization ( writer->localization ) );
	for ( int i = 1; i < writer->memory->ElementsCount (); i++ )
		width = Max ( width, GetElementField ( writer->memory->Element ( i ), i ).size () );
}

void KnowledgeBaseWriterText::FieldWriter::TitleBarPrepare ()
{
	title = StringLines ( GetLocalization ( writer->localization ) );
	nextLine.clear ();
}

void KnowledgeBaseWriterText::FieldWriter::TitleBarFetchNextLine ()
{
	nextLine = NextMeanLine ( title );
}

bool KnowledgeBaseWriterText::FieldWriter::TitleBarCheck ()
{
	return nextLine.empty ();
}

void KnowledgeBaseWriterText::FieldWriter::TitleBarWrite ()
{
	std::stringstream ss;
	ss << " | " << std::setw ( width ) << nextLine;
	writer->stream->WriteString ( ss.str () );
}

void KnowledgeBaseWriterText::FieldWriter::WriteDelimiter ()
{
	writer->stream->WriteString ( "-+-" + std::string ( width, '-' ) );
}

void KnowledgeBaseWriterText::FieldWriter::WriteMemoryElement ( int globalAddress )
{
	std::stringstream ss;	
	ss << " | " << std::setw ( width ) << GetElementField ( writer->memory->Element ( globalAddress ), globalAddress );
	writer->stream->WriteString ( ss.str () );
}

KnowledgeBaseMemoryInterfacePtr KnowledgeBaseWriterText::FieldWriter::Memory ()
{
	return writer->memory;
}

KnowledgeBaseWriterTextLocalizationInterfacePtr KnowledgeBaseWriterText::FieldWriter::Localization ()
{
	return writer->localization;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define IMPLEMENT_FIELD_WRITER(name) \
	std::string KnowledgeBaseWriterText::FieldWriter_##name::GetLocalization ( KnowledgeBaseWriterTextLocalizationInterfacePtr localization ) \
	{ \
		return localization->name (); \
	} \
	std::string KnowledgeBaseWriterText::FieldWriter_##name::GetElementField ( KnowledgeBaseElementInterfacePtr element, int globalAddress ) \
	{ \
		return ToStr ( element->name () ); \
	}
	
	IMPLEMENT_FIELD_WRITER ( SeniorKey );
	IMPLEMENT_FIELD_WRITER ( SeniorType );
	IMPLEMENT_FIELD_WRITER ( UpperBranch );
	IMPLEMENT_FIELD_WRITER ( LowerBranch );
	IMPLEMENT_FIELD_WRITER ( NextLevel );
	IMPLEMENT_FIELD_WRITER ( Synonym );
	IMPLEMENT_FIELD_WRITER ( AdjacentAscending );
	IMPLEMENT_FIELD_WRITER ( AdjacentDescending );
	IMPLEMENT_FIELD_WRITER ( BeforeAdjacentAscending );
	IMPLEMENT_FIELD_WRITER ( BeforeAdjacentDescending );
	IMPLEMENT_FIELD_WRITER ( GreaterAscending );
	IMPLEMENT_FIELD_WRITER ( GreaterDescending );
	IMPLEMENT_FIELD_WRITER ( LesserAscending );
	IMPLEMENT_FIELD_WRITER ( LesserDescending );
	
#undef IMPLEMENT_FIELD_WRITER

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string KnowledgeBaseWriterText::FieldWriter_LexemText::GetLocalization ( KnowledgeBaseWriterTextLocalizationInterfacePtr localization )
{
	return localization->LexemText ();
}

std::string KnowledgeBaseWriterText::FieldWriter_LexemText::GetElementField ( KnowledgeBaseElementInterfacePtr element, int globalAddress )
{
	if ( globalAddress == Memory ()->TypeRootAddress () )
		return Localization ()->TypeRoot ();
	else if ( globalAddress == Memory ()->ValueRootAddress () )
		return Localization ()->ValueRoot ();
	else
		return ToStr ( element->LexemText () );
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
